#include "AbstractViewport.h"
#include <iostream>
#include <stdlib.h>

namespace k2graphics 
{

AbstractViewport::AbstractViewport(int numLayers)
{
	for(int i = 0; i < numLayers; ++i)
	{
		RenderLayer* tempLayer = new RenderLayer();
		tempLayer->initMax(MAX_OBJS_PER_LAYER);
		tempLayer->setParallax(1.0);
		layers.push_back(tempLayer);
	}

	viewportX = 0.0;
	viewportY = 0.0;
	viewportWidth = 10.0;
	viewportHeight = 10.0;

	viewportSettings = new float[5];
	numSettings = 5;

	viewportGroup = 0;
	viewportMask = 0;

	contentUpdated = true;
	isNetworkCloned = false;
}

void AbstractViewport::clearObjects()
{
	for(int i = 0; i < layers.size(); ++i)
		layers[i]->clear();
}

void AbstractViewport::pushObject(RenderObject* ro)
{
	// first, check to see if this viewport should even see this object
	if(viewportGroup != ro->viewportGroup && !(ro->viewportBits & viewportMask))
		return;

	int layer = ro->layer;

	//std::cout << "Layer: " << layer << std::endl;

	// here we must check the object against its layer viewport position
	double vcx = viewportX * layers[layer]->invLayerParallax;
	double vcy = viewportY * layers[layer]->invLayerParallax;

	double lhw = viewportWidth / 2.0;
	double lhh = viewportHeight / 2.0;

	// test stuff
	double dx = abs(vcx - ro->roX);
	double dy = abs(vcy - ro->roY);

	// if it is too far away vertically or horizontally, don't actually push it
	if(dx > lhw + ro->roHalfWidth || dy > lhh + ro->roHalfHeight)
		return;

	//std::cout << "Object added!\n";

	// otherwise, add it
	layers[layer]->addObj(ro->rd);
}

void AbstractViewport::pushObjectNoQuestions(RenderObject* ro)
{
	// just directly add the object (for local render objects which should always be pushed)
	// (maybe that should change)
	layers[ro->layer]->addObj(ro->rd);
}

void AbstractViewport::setPosition(float nx, float ny)
{
	viewportX = nx;
	viewportY = ny;
}

void AbstractViewport::setDimensions(float nwidth, float nheight)
{
	viewportWidth = nwidth;
	viewportHeight = nheight;
}

void AbstractViewport::deleteAllLayerData()
{
	for(int i = 0; i < layers.size(); ++i)
		layers[i]->deleteObjects();
}

// some helper functions
/*
void writeFloats(unsigned char* dest, float* f, int numFloats)
{
	int* fi = reinterpret_cast<int*>(f);

	for(int i = 0; i < numFloats; ++i)
	{
		SDLNet_Write32(fi[i], dest + (4 * i));
	}
}

void readFloats(unsigned char* src, float* f, int numFloats)
{
	int* fi = reinterpret_cast<int*>(f);

	for(int i = 0; i < numFloats; ++i)
	{
		fi[i] = SDLNet_Read32(src + (4 * i));
	}
}

void writeInts(unsigned char* dest, int* i, int numInts)
{
	for(int j = 0; j < numInts; ++j)
	{
		SDLNet_Write32(i[j], dest + (4 * j));
	}
}

void readInts(unsigned char* src, int* i, int numInts)
{
	for(int j = 0; j < numInts; ++j)
	{
		i[j] = SDLNet_Read32(src + (4 * j));
	}
}

void writeBytes(unsigned char* dest, unsigned char* b, int numBytes)
{
	for(int i = 0; i < numBytes; ++i)
		dest[i] = b[i];
}

void readBytes(unsigned char* src, unsigned char* b, int numBytes)
{
	for(int i = 0; i < numBytes; ++i)
		b[i] = src[i];
}

int AbstractViewport::writeToByteArray(unsigned char* dest, int maxBytes)
{
	int bytePos = 0;
	unsigned char temp = 0;
	float tempf[] = {viewportX, viewportY, 0.0, viewportWidth, viewportHeight};
	// first, write number of layers
	dest[bytePos] = (unsigned char)(layers.size());
	++bytePos;
	// next, write x, y, theta, width, height
	writeFloats(dest + bytePos, tempf, 5);
	bytePos += 5 * 4;
	// next, do the layers one at a time
	for(int i = 0; i < layers.size(); ++i)
	{
		// write the number of objects
		int numObjects = layers[i]->numObjects;
		dest[bytePos] = (unsigned char)numObjects;
		++bytePos;

		// write the objects themselves
		for(int j = 0; j < numObjects; ++j)
		{
			RenderData* rd = layers[i]->objects[j];
			// write the target
			dest[bytePos] = (unsigned char)(rd->renderTarget);
			++bytePos;
			// write the number of bytes
			dest[bytePos] = (unsigned char)(rd->numBytes);
			++bytePos;
			// write the number of ints
			dest[bytePos] = (unsigned char)(rd->numInts);
			++bytePos;
			// write the number of floats
			dest[bytePos] = (unsigned char)(rd->numFloats);
			++bytePos;
			// write the bytes
			writeBytes(dest + bytePos, rd->byteData, rd->numBytes);
			bytePos += rd->numBytes;
			// write the ints
			writeInts(dest + bytePos, rd->intData, rd->numInts);
			bytePos += rd->numInts * 4;
			// write the floats
			writeFloats(dest + bytePos, rd->floatData, rd->numFloats);
			bytePos += rd->numFloats * 4;
		}
	}
	return bytePos;
}

int AbstractViewport::readFromByteArray(unsigned char* src, int numBytes)
{
	float tempf[5];

	// first, read the number of layers
	int bytePos = 0;
	int numLayers = src[bytePos];
	++bytePos;
	// next read x, y, theta, width, height
	readFloats(src + bytePos, tempf, 5);
	bytePos += 5 * 4;
	viewportX = tempf[0];
	viewportY = tempf[1];
	viewportWidth = tempf[3];
	viewportHeight = tempf[4];
	int totalObjects = 0;
	// read the layers one by one
	for(int i = 0; i < numLayers; ++i)
	{
		// read number of objects in this layer
		int numObjs = src[bytePos];
		totalObjects += numObjs;
		++bytePos;

		// read in objects
		for(int j = 0; j < numObjs; ++j)
		{
			// read in target
			int renderTarget = src[bytePos];
			++bytePos;

			// number of bytes
			int numBytes = src[bytePos];
			++bytePos;

			// number of ints
			int numInts = src[bytePos];
			++bytePos;

			// number of floats
			int numFloats = src[bytePos];
			++bytePos;

			// create render object
			RenderData* rd = new RenderData(renderTarget, numBytes, numInts, numFloats);

			// read in bytes
			readBytes(src + bytePos, rd->byteData, numBytes);
			bytePos += numBytes;

			// read in ints
			readInts(src + bytePos, rd->intData, numInts);
			bytePos += numInts * 4;

			// read in floats
			readFloats(src + bytePos, rd->floatData, numFloats);
			bytePos += numFloats * 4;

			// set values
			rd->numBytes = numBytes;
			rd->numInts = numInts;
			rd->numFloats = numFloats;

			// add rd
			layers[i]->addObj(rd);
		}
	}

	//std::cout << "Read " << numLayers << " layers with " << totalObjects << " objects.\n";

	return bytePos;
}*/

void AbstractViewport::setGroup(int g)
{
	viewportGroup = g;
}

void AbstractViewport::setMask(int m)
{
	viewportMask = m;
}

PosLoc2D AbstractViewport::getPosition()
{
	return PosLoc2D(viewportX, viewportY);
}

PosLoc2D AbstractViewport::getDimensions()
{
	return PosLoc2D(viewportWidth, viewportHeight);
}

RenderLayer* AbstractViewport::getLayer(int layer)
{
	return layers[layer];
}

void AbstractViewport::writeSettings()
{
	// settings should only have five values...
	// x, y, width, height, numlayers
	viewportSettings[0] = viewportX;
	viewportSettings[1] = viewportY;
	viewportSettings[2] = viewportWidth;
	viewportSettings[3] = viewportHeight;
	viewportSettings[4] = layers.size();
	//std::cout << "vpX: " << viewportX << "vpY: " << viewportY << std::endl;
	//std::cout << "vpW: " << viewportWidth << "vpH: " << viewportHeight << std::endl;
}

float* AbstractViewport::getSettings()
{
	return viewportSettings;
}

int AbstractViewport::getNumSettings()
{
	return numSettings;
}

void AbstractViewport::debugPrintEverything()
{
	std::cout << "Num layers: " << layers.size() << std::endl;
	for(int i = 0; i < layers.size(); ++i)
	{
		std::cout << "Layer " << i << " has " << layers[i]->numObjects << " objects.\n";
		for(int j = 0; j < layers[i]->numObjects; ++j)
		{
			std::cout << "Object " << j << std::endl;
			layers[i]->objects[j]->debugPrintData();
		}
	}
}

/*
void AbstractViewport::genTestData()
{
	deleteAllLayerData();
	for(int i = 0; i < 5; ++i)
	{
		for(int j = 0; j < i; ++j)
		{
			RenderData* rd = new RenderData(3, 20, 20, 20);
			rd->genTestData(i + j);
			layers[i]->addObj(rd);
		}
	}

	debugPrintEverything();

	unsigned char buf[10000];

	int writtenBytes = writeToByteArray(buf, 10000);
	std::cout << "Wrote " << writtenBytes << " bytes.\n";
	deleteAllLayerData();
	int readBytes = readFromByteArray(buf, 10000);
	std::cout << "Read " << readBytes << " bytes.\n";
	debugPrintEverything();
}*/

}